/*
 * Copyright (c) 2015, EURECOM (www.eurecom.fr)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are
 * those of the authors and should not be interpreted as representing official
 * policies, either expressed or implied, of the FreeBSD Project.
 */

#if HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdint.h>
#include <stdlib.h>

#define G_LOG_DOMAIN ("UI_CB")

#include <gtk/gtk.h>

#include "logs.h"
#include "rc.h"

#include "socket.h"

#include "ui_callbacks.h"
#include "ui_filters.h"
#include "ui_interface.h"
#include "ui_main_screen.h"
#include "ui_menu_bar.h"
#include "ui_notebook.h"
#include "ui_notif_dlg.h"
#include "ui_notifications.h"
#include "ui_signal_dissect_view.h"
#include "ui_tree_view.h"

#include "locate_root.h"
#include "types.h"
#include "xml_parse.h"

static gboolean refresh_message_list = TRUE;
static gboolean filters_changed = FALSE;
static gboolean operation_running = FALSE;

static const char *ui_ip;
static uint16_t ui_port;
static GtkWidget *dialogbox_connect;
static gboolean ui_auto_reconnect;

gboolean ui_callback_on_open_messages(GtkWidget *widget, gpointer data) {
  gboolean refresh = (data != NULL) ? TRUE : FALSE;

  g_message("Open messages event occurred %d", refresh);

  if (operation_running == FALSE) {
    operation_running = TRUE;

    if (refresh && (ui_main_data.messages_file_name != NULL)) {
      ui_messages_read(ui_main_data.messages_file_name);
    } else {
      ui_messages_open_file_chooser();
    }

    operation_running = FALSE;
  }

  return TRUE;
}

gboolean ui_callback_on_save_messages(GtkWidget *widget, gpointer data) {
  gboolean filtered = (data != NULL) ? TRUE : FALSE;

  g_message("Save messages event occurred %d", filtered);

  if (operation_running == FALSE) {
    operation_running = TRUE;
    ui_messages_save_file_chooser(filtered);
    operation_running = FALSE;
  }

  return TRUE;
}

gboolean ui_callback_on_filters_enabled(GtkToolButton *button, gpointer data) {
  gboolean enabled;
  gboolean changed;

  enabled = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(button));
  g_info("Filters enabled event occurred %d", enabled);
  changed = ui_filters_enable(enabled);

  if (changed) {
    /*
     * Set the tool tip text
     */
    if (enabled) {
      gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(button),
                                     "Disable messages filtering");
    } else {
      gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(button),
                                     "Enable messages filtering");
    }

    ui_tree_view_refilter();
  }

  return TRUE;
}

gboolean ui_callback_on_open_filters(GtkWidget *widget, gpointer data) {
  gboolean refresh = (data != NULL) ? TRUE : FALSE;

  g_message("Open filters event occurred");

  if (operation_running == FALSE) {
    operation_running = TRUE;

    if (refresh && (ui_main_data.filters_file_name != NULL)) {
      ui_filters_read(ui_main_data.filters_file_name);
    } else {
      ui_filters_open_file_chooser();
    }

    operation_running = FALSE;
  }

  return TRUE;
}

gboolean ui_callback_on_save_filters(GtkWidget *widget, gpointer data) {
  if (operation_running == FALSE) {
    operation_running = TRUE;
    g_message("Save filters event occurred");
    ui_filters_save_file_chooser();
    operation_running = FALSE;
  }

  return TRUE;
}

gboolean ui_callback_on_enable_filters(GtkWidget *widget, gpointer data) {
  gboolean enabled;

  enabled = gtk_toggle_tool_button_get_active(
      GTK_TOGGLE_TOOL_BUTTON(ui_main_data.filters_enabled));
  gtk_toggle_tool_button_set_active(
      GTK_TOGGLE_TOOL_BUTTON(ui_main_data.filters_enabled), !enabled);
  return TRUE;
}

gboolean ui_callback_on_about(GtkWidget *widget, gpointer data) {
#if defined(PACKAGE_STRING)
  ui_notification_dialog(GTK_MESSAGE_INFO, FALSE, "about", "Eurecom %s",
                         PACKAGE_STRING);
#else
  ui_notification_dialog(GTK_MESSAGE_INFO, FALSE, "about",
                         "Eurecom itti_analyzer");
#endif
  return TRUE;
}

gint ui_callback_check_string(const char *string, const gint lenght,
                              const guint message_number) {
  gint real_length;

  real_length = strnlen(string, lenght);

  if (lenght != real_length) {
    if (message_number) {
      g_warning(
          "Bad string format for message %u, '\\0' found before end of string "
          "(%d/%d)\n",
          message_number, real_length, lenght);
    } else {
      g_warning("Bad string format, '\\0' found before end of string (%d/%d)\n",
                real_length, lenght);
    }

    return (real_length);
  } else {
    return (lenght);
  }
}

gboolean ui_callback_on_select_signal(GtkTreeSelection *selection,
                                      GtkTreeModel *model, GtkTreePath *path,
                                      gboolean path_currently_selected,
                                      gpointer user_data) {
  static gpointer buffer_current;
  ui_text_view_t *text_view;
  GtkTreeIter iter;

  g_debug("Message selected %d %p %p %s", path_currently_selected,
          buffer_current, path, gtk_tree_path_to_string(path));

  if (!path_currently_selected) {
    text_view = (ui_text_view_t *)user_data;
    g_assert(text_view != NULL);

    if (gtk_tree_model_get_iter(model, &iter, path)) {
      gpointer buffer;
      uint32_t message_number;
      uint32_t message_id;
      uint32_t origin_task_id;
      uint32_t destination_task_id;
      char *instance_name;
      uint32_t instance;
      char label[100];

      gtk_tree_model_get(model, &iter, COL_MSG_NUM, &message_number,
                         COL_MESSAGE_ID, &message_id, COL_FROM_TASK_ID,
                         &origin_task_id, COL_TO_TASK_ID, &destination_task_id,
                         COL_INSTANCE, &instance_name, COL_INSTANCE_ID,
                         &instance, COL_BUFFER, &buffer, -1);
      g_debug("  Get iter %p %p", buffer_current, buffer);

      if (ui_tree_view_last_event) {
        g_debug("last_event %p %d %d", ui_tree_view_last_event,
                ui_tree_view_last_event->type, ui_tree_view_last_event->button);

        if (ui_tree_view_last_event->type == GDK_BUTTON_PRESS) {
          /*
           * Callback is due to a button click
           */
          if (ui_tree_view_last_event->button == 1) {
            /*
             * It was a left mouse click
             */
            gtk_toggle_tool_button_set_active(
                GTK_TOGGLE_TOOL_BUTTON(ui_main_data.signals_go_to_last_button),
                FALSE);
          }

          if (ui_tree_view_last_event->button == 3) {
            /*
             * It was a right mouse click
             */
            int item;

            /*
             * Clear event
             */
            ui_tree_view_last_event = NULL;
            /*
             * Create filter menus to refers its items in the pop-up menu
             */
            ui_create_filter_menus();
            g_info("Message selected right click %d %d %d %d", message_id,
                   origin_task_id, destination_task_id, instance);
            /*
             * Message Id menu
             */
            {
              /*
               * Invalidate associated menu item to avoid issue with call back
               * when updating the menu item check state
               */
              ui_tree_view_menu_enable[MENU_MESSAGE].filter_item = NULL;
              item = ui_filters_search_id(&ui_filters.messages, message_id);
              /*
               * Update the menu item check state based on message ID state
               */
              gtk_check_menu_item_set_active(
                  GTK_CHECK_MENU_ITEM(
                      ui_tree_view_menu_enable[MENU_MESSAGE].menu_enable),
                  ui_filters.messages.items[item].enabled);
              /*
               * Set menu item label
               */
              sprintf(label, "Message:  %s", message_id_to_string(message_id));
              gtk_menu_item_set_label(
                  GTK_MENU_ITEM(
                      ui_tree_view_menu_enable[MENU_MESSAGE].menu_enable),
                  label);
              /*
               * Save menu item associated to this row
               */
              ui_tree_view_menu_enable[MENU_MESSAGE].filter_item =
                  &ui_filters.messages.items[item];
            }
            /*
             * Origin task id
             */
            {
              /*
               * Invalidate associated menu item to avoid issue with call back
               * when updating the menu item check state
               */
              ui_tree_view_menu_enable[MENU_FROM_TASK].filter_item = NULL;
              item = ui_filters_search_id(&ui_filters.origin_tasks,
                                          origin_task_id);
              /*
               * Update the menu item check state based on message ID state
               */
              gtk_check_menu_item_set_active(
                  GTK_CHECK_MENU_ITEM(
                      ui_tree_view_menu_enable[MENU_FROM_TASK].menu_enable),
                  ui_filters.origin_tasks.items[item].enabled);
              /*
               * Set menu item label
               */
              sprintf(label, "From:  %s",
                      task_id_to_string(origin_task_id, origin_task_id_type));
              gtk_menu_item_set_label(
                  GTK_MENU_ITEM(
                      ui_tree_view_menu_enable[MENU_FROM_TASK].menu_enable),
                  label);
              /*
               * Save menu item associated to this row
               */
              ui_tree_view_menu_enable[MENU_FROM_TASK].filter_item =
                  &ui_filters.origin_tasks.items[item];
            }
            /*
             * Destination task id
             */
            {
              /*
               * Invalidate associated menu item to avoid issue with call back
               * when updating the menu item check state
               */
              ui_tree_view_menu_enable[MENU_TO_TASK].filter_item = NULL;
              item = ui_filters_search_id(&ui_filters.destination_tasks,
                                          destination_task_id);
              /*
               * Update the menu item check state based on message ID state
               */
              gtk_check_menu_item_set_active(
                  GTK_CHECK_MENU_ITEM(
                      ui_tree_view_menu_enable[MENU_TO_TASK].menu_enable),
                  ui_filters.destination_tasks.items[item].enabled);
              /*
               * Set menu item label
               */
              sprintf(label, "To:  %s",
                      task_id_to_string(destination_task_id,
                                        destination_task_id_type));
              gtk_menu_item_set_label(
                  GTK_MENU_ITEM(
                      ui_tree_view_menu_enable[MENU_TO_TASK].menu_enable),
                  label);
              /*
               * Save menu item associated to this row
               */
              ui_tree_view_menu_enable[MENU_TO_TASK].filter_item =
                  &ui_filters.destination_tasks.items[item];
            }
            /*
             * Instance
             */
            {
              /*
               * Invalidate associated menu item to avoid issue with call back
               * when updating the menu item check state
               */
              ui_tree_view_menu_enable[MENU_INSTANCE].filter_item = NULL;
              item = ui_filters_search_id(&ui_filters.instances, instance);
              /*
               * Update the menu item check state based on message ID state
               */
              gtk_check_menu_item_set_active(
                  GTK_CHECK_MENU_ITEM(
                      ui_tree_view_menu_enable[MENU_INSTANCE].menu_enable),
                  ui_filters.instances.items[item].enabled);
              /*
               * Set menu item label
               */
              sprintf(label, "Instance:  %s", instance_name);
              gtk_menu_item_set_label(
                  GTK_MENU_ITEM(
                      ui_tree_view_menu_enable[MENU_INSTANCE].menu_enable),
                  label);
              /*
               * Save menu item associated to this row
               */
              ui_tree_view_menu_enable[MENU_INSTANCE].filter_item =
                  &ui_filters.instances.items[item];
            }
            gtk_menu_popup(GTK_MENU(ui_tree_view_menu), NULL, NULL, NULL, NULL,
                           0, gtk_get_current_event_time());
          }
        }

        /*
         * Clear event
         */
        ui_tree_view_last_event = NULL;
      }

      if (buffer_current != buffer) {
        buffer_current = buffer;
        /*
         * Clear the view
         */
        CHECK_FCT_DO(ui_signal_dissect_clear_view(text_view), return FALSE);

        if (ui_main_data.display_message_header) {
          CHECK_FCT_DO(dissect_signal_header((buffer_t *)buffer,
                                             ui_signal_set_text, text_view),
                       return FALSE);
        }

        if ((strcmp(message_id_to_string(message_id), "ERROR_LOG") == 0) ||
            (strcmp(message_id_to_string(message_id), "WARNING_LOG") == 0) ||
            (strcmp(message_id_to_string(message_id), "NOTICE_LOG") == 0) ||
            (strcmp(message_id_to_string(message_id), "INFO_LOG") == 0) ||
            (strcmp(message_id_to_string(message_id), "DEBUG_LOG") == 0) ||
            (strcmp(message_id_to_string(message_id), "GENERIC_LOG") == 0)) {
          gchar *data;
          gint data_size;
          uint32_t message_header_type_size;

          if (ui_main_data.display_message_header) {
            ui_signal_set_text(text_view, "\n", 1);
          }

          message_header_type_size = get_message_header_type_size();
          data = (gchar *)buffer_at_offset((buffer_t *)buffer,
                                           message_header_type_size);
          data_size = get_message_size((buffer_t *)buffer);
          data_size = ui_callback_check_string(data, data_size, message_number);
          g_info(
              "    dump message %d: header type size: %u, data size: %u, "
              "buffer %p, follow last %d",
              message_number, message_header_type_size, data_size, buffer,
              ui_main_data.follow_last);
          ui_signal_set_text(text_view, data, data_size);
        } else {
          g_info("    dissect message %d: id %d, buffer %p, follow last %d",
                 message_number, message_id, buffer, ui_main_data.follow_last);
          /*
           * Dissect the signal
           */
          CHECK_FCT_DO(
              dissect_signal((buffer_t *)buffer, ui_signal_set_text, text_view),
              return FALSE);
        }
      }
    }
  }

  return TRUE;
}

gboolean ui_callback_on_menu_enable(GtkWidget *widget, gpointer data) {
  ui_tree_view_menu_enable_t *menu_enable = data;

  if (menu_enable->filter_item != NULL) {
    gtk_check_menu_item_set_active(
        GTK_CHECK_MENU_ITEM(menu_enable->filter_item->menu_item),
        gtk_check_menu_item_get_active(
            GTK_CHECK_MENU_ITEM(menu_enable->menu_enable)));
    menu_enable->filter_item = NULL;
  }

  return TRUE;
}

gboolean ui_callback_on_menu_color(GtkWidget *widget, gpointer data) {
  ui_tree_view_menu_color_t *menu_color = data;
  GdkRGBA color;
  GtkWidget *color_chooser;
  gint response;

  color_chooser = gtk_color_chooser_dialog_new(
      "Select message background color", GTK_WINDOW(ui_main_data.window));
  gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(color_chooser), FALSE);
  response = gtk_dialog_run(GTK_DIALOG(color_chooser));

  if (response == GTK_RESPONSE_OK) {
    int red, green, blue;
    char *color_string;

    color_string = menu_color->foreground
                       ? menu_color->menu_enable->filter_item->foreground
                       : menu_color->menu_enable->filter_item->background;
    gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(color_chooser), &color);
    red = (int)(color.red * 255);
    green = (int)(color.green * 255);
    blue = (int)(color.blue * 255);
    snprintf(color_string, COLOR_SIZE, "#%02x%02x%02x", red, green, blue);
    ui_tree_view_refilter();
    g_info("Selected color for %s %f->%02x %f->%02x %f->%02x %s",
           menu_color->menu_enable->filter_item->name, color.red, red,
           color.green, green, color.blue, blue, color_string);
  }

  gtk_widget_destroy(color_chooser);
  return TRUE;
}

void ui_signal_add_to_list(gpointer data, gpointer user_data) {
  gboolean goto_last = user_data ? TRUE : FALSE;
  buffer_t *signal_buffer;
  GtkTreePath *path;
  GtkTreeViewColumn *focus_column;
  uint32_t lte_frame;
  uint32_t lte_slot;
  uint32_t origin_task_id;
  uint32_t destination_task_id;
  uint32_t instance;
  char lte_time[15];

  g_assert(data != NULL);
  g_assert(origin_task_id_type != NULL);
  g_assert(destination_task_id_type != NULL);
  gtk_tree_view_get_cursor(GTK_TREE_VIEW(ui_main_data.messages_list), &path,
                           &focus_column);
  signal_buffer = (buffer_t *)data;
  lte_frame = get_lte_frame(signal_buffer);
  lte_slot = get_lte_slot(signal_buffer);
  sprintf(lte_time, "%d.%02d", lte_frame, lte_slot);
  get_message_id(root, signal_buffer, &signal_buffer->message_id);
  origin_task_id = get_task_id(signal_buffer, origin_task_id_type);
  destination_task_id = get_task_id(signal_buffer, destination_task_id_type);
  instance = get_instance(signal_buffer);
  ui_tree_view_new_signal_ind(
      signal_buffer->message_number, lte_time, signal_buffer->message_id,
      message_id_to_string(signal_buffer->message_id), origin_task_id,
      task_id_to_string(origin_task_id, origin_task_id_type),
      destination_task_id,
      task_id_to_string(destination_task_id, destination_task_id_type),
      instance, data);

  if ((strcmp(message_id_to_string(signal_buffer->message_id), "ERROR_LOG") ==
       0) ||
      (strcmp(message_id_to_string(signal_buffer->message_id), "WARNING_LOG") ==
       0) ||
      (strcmp(message_id_to_string(signal_buffer->message_id), "NOTICE_LOG") ==
       0) ||
      (strcmp(message_id_to_string(signal_buffer->message_id), "INFO_LOG") ==
       0) ||
      (strcmp(message_id_to_string(signal_buffer->message_id), "DEBUG_LOG") ==
       0) ||
      (strcmp(message_id_to_string(signal_buffer->message_id), "GENERIC_LOG") ==
       0)) {
    gchar *string_terminal;
    gint string_terminal_size;
    uint32_t message_header_type_size;

    message_header_type_size = get_message_header_type_size();
    string_terminal = (gchar *)buffer_at_offset((buffer_t *)signal_buffer,
                                                message_header_type_size);
    string_terminal_size = get_message_size((buffer_t *)signal_buffer);
    string_terminal_size = ui_callback_check_string(
        string_terminal, string_terminal_size, signal_buffer->message_number);
    ui_notebook_terminal_append_data(string_terminal, string_terminal_size);
  }

  /*
   * Increment number of messages
   */
  ui_main_data.nb_message_received++;

  if ((ui_main_data.follow_last) && (goto_last)) {
    /*
     * Advance to the new last signal
     */
    ui_tree_view_select_row(ui_tree_view_get_filtered_number() - 1);
  }
}

static gboolean ui_handle_update_signal_list(gint fd, void *data,
                                             size_t data_length) {
  pipe_new_signals_list_message_t *signal_list_message;

  /*
   * Enable buttons to move in the list of signals
   */
  ui_set_sensitive_move_buttons(TRUE);
  signal_list_message = (pipe_new_signals_list_message_t *)data;
  g_assert(signal_list_message != NULL);
  g_assert(signal_list_message->signal_list != NULL);
  g_list_foreach(signal_list_message->signal_list, ui_signal_add_to_list,
                 (gpointer)TRUE);
  /*
   * Free the list but not user data associated with each element
   */
  g_list_free(signal_list_message->signal_list);
  /*
   * Free the message
   */
  free(signal_list_message);
  ui_gtk_flush_events();
  return TRUE;
}

static gboolean ui_handle_socket_connection_failed(gint fd) {
  gtk_dialog_response(GTK_DIALOG(dialogbox_connect), GTK_RESPONSE_REJECT);
  return TRUE;
}

static gboolean ui_handle_socket_connection_lost(gint fd) {
  if (operation_running) {
    operation_running = FALSE;

    if (ui_auto_reconnect) {
      ui_callback_on_connect(NULL, (gpointer)FALSE);
    } else {
      ui_enable_connect_button();
      ui_set_sensitive_save_message_buttons(TRUE);
    }
  }

  return TRUE;
}

static gboolean ui_handle_socket_xml_definition(gint fd, void *data,
                                                size_t data_length) {
  pipe_xml_definition_message_t *xml_definition_message;

  xml_definition_message = (pipe_xml_definition_message_t *)data;
  g_assert(xml_definition_message != NULL);
  g_assert(data_length == sizeof(pipe_xml_definition_message_t));
  xml_parse_buffer(xml_definition_message->xml_definition,
                   xml_definition_message->xml_definition_length);
  return TRUE;
}

gboolean ui_pipe_callback(gint source, gpointer user_data) {
  void *input_data = NULL;
  size_t input_data_length = 0;
  pipe_input_header_t input_header;

  /*
   * Read the header
   */
  if (read(source, &input_header, sizeof(input_header)) < 0) {
    g_warning("Failed to read from pipe %d: %s", source, g_strerror(errno));
    return FALSE;
  }

  input_data_length = input_header.message_size - sizeof(input_header);

  /*
   * Checking for non-header part
   */
  if (input_data_length > 0) {
    input_data = malloc(input_data_length);

    if (read(source, input_data, input_data_length) < 0) {
      g_warning("Failed to read from pipe %d: %s", source, g_strerror(errno));
      return FALSE;
    }
  }

  switch (input_header.message_type) {
    case UI_PIPE_CONNECTION_FAILED:
      return ui_handle_socket_connection_failed(source);

    case UI_PIPE_CONNECTION_LOST:
      return ui_handle_socket_connection_lost(source);

    case UI_PIPE_XML_DEFINITION:
      gtk_dialog_response(GTK_DIALOG(dialogbox_connect), GTK_RESPONSE_OK);
      return ui_handle_socket_xml_definition(source, input_data,
                                             input_data_length);

    case UI_PIPE_UPDATE_SIGNAL_LIST:
      return ui_handle_update_signal_list(source, input_data,
                                          input_data_length);

    default:
      g_warning("[gui] Unhandled message type %u", input_header.message_type);
      g_assert_not_reached();
  }

  return FALSE;
}

gboolean ui_callback_on_auto_reconnect(GtkWidget *widget, gpointer data) {
  gboolean enabled;
  gboolean changed = TRUE;
  gboolean toggle = (data != NULL) ? TRUE : FALSE;

  enabled = gtk_toggle_tool_button_get_active(
      GTK_TOGGLE_TOOL_BUTTON(ui_main_data.auto_reconnect));

  if (toggle) {
    gtk_toggle_tool_button_set_active(
        GTK_TOGGLE_TOOL_BUTTON(ui_main_data.auto_reconnect), !enabled);
    return TRUE;
  }

  g_info("Auto reconnect event occurred %d %d ", toggle, enabled);
  ui_auto_reconnect = enabled;

  if (changed) {
    /*
     * Set the tool tip text
     */
    if (enabled) {
      gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(ui_main_data.auto_reconnect),
                                     "Disable automatic reconnection");
    } else {
      gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(ui_main_data.auto_reconnect),
                                     "Enable automatic reconnection");
    }
  }

  return TRUE;
}

void ui_callback_dialogbox_connect_destroy(void) {
  if (dialogbox_connect != NULL) {
    gtk_widget_destroy(dialogbox_connect);
    dialogbox_connect = NULL;
    g_message("Connect dialogbox destroyed");
  }
}

gboolean ui_callback_on_connect(GtkWidget *widget, gpointer data) {
  /*
   * We have to retrieve the ip address and ui_port of remote host
   */
  gboolean start = (data != NULL) ? TRUE : FALSE;
  int pipe_fd[2];

  if (start) {
    ui_port = atoi(gtk_entry_get_text(GTK_ENTRY(ui_main_data.port_entry)));
    ui_ip = gtk_entry_get_text(GTK_ENTRY(ui_main_data.ip_entry));
  }

  g_message("Connect event occurred to %s:%d %s", ui_ip, ui_port,
            start ? "START" : "RETRY");

  if (start) {
    if (strlen(ui_ip) == 0) {
      ui_notification_dialog(GTK_MESSAGE_ERROR, FALSE, "Connect",
                             "Empty host ip address");
      return FALSE;
    }

    if (ui_port == 0) {
      ui_notification_dialog(GTK_MESSAGE_ERROR, FALSE, "Connect",
                             "Invalid host ui_port value");
      return FALSE;
    }
  }

  if (operation_running == FALSE) {
    operation_running = TRUE;
    ui_pipe_new(pipe_fd, ui_pipe_callback, NULL);
    memcpy(ui_main_data.pipe_fd, pipe_fd, sizeof(int) * 2);
    /*
     * Disable the connect button
     */
    ui_disable_connect_button();
    ui_set_title("connecting to %s:%d ...", ui_ip, ui_port);
    {
      const static char *message_formats[] = {
          "Connecting to %s:%d ...",
          "Connection lost!\n\n"
          "Trying to reconnect to %s:%d ..."};
      gint response;
      gint x, y;
      gint w, h;

      if (socket_connect_to_remote_host(ui_ip, ui_port, pipe_fd[1]) != 0) {
        ui_enable_connect_button();
        operation_running = FALSE;
        return FALSE;
      }

      /*
       * Create dialog box for connect message:
       * * * - non modal mode does not seems to work, don't set the parent
       * window to allow some interactions with it!
       */
      dialogbox_connect = gtk_message_dialog_new(
          NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_OTHER,
          GTK_BUTTONS_CANCEL, message_formats[start ? 0 : 1], ui_ip, ui_port);
      gtk_window_set_title(GTK_WINDOW(dialogbox_connect), "Connect");
      /*
       * Set the window at center of main window manually as there is no parent
       * defined
       */
      gtk_window_get_position(GTK_WINDOW(ui_main_data.window), &x, &y);
      gtk_window_get_size(GTK_WINDOW(ui_main_data.window), &w, &h);
      g_message("Main window position: %d,%d dimension: %d,%d", x, y, w, h);
      x += w / 2;
      y += h / 2;
      g_message("Connect window position %d,%d", x, y);
      gtk_window_set_gravity(GTK_WINDOW(dialogbox_connect), GDK_GRAVITY_CENTER);
      gtk_window_move(GTK_WINDOW(dialogbox_connect), x, y);
      response = gtk_dialog_run(GTK_DIALOG(dialogbox_connect));
      g_message("Connect dialog response %s (%d)",
                gtk_get_respose_string(response), response);

      if (response == GTK_RESPONSE_NONE) {
        /*
         * Dialogbox has been destroyed when program is exited, do nothing
         */
        return (TRUE);
      }

      if (response == GTK_RESPONSE_OK) {
        /*
         * Connection is established
         */
        ui_set_sensitive_save_message_buttons(FALSE);
        ui_callback_signal_clear_list(NULL, NULL);
        ui_set_title("%s:%d", ui_ip, ui_port);
      } else {
        /*
         * Connection can not be established
         */
        if (response == GTK_RESPONSE_REJECT) {
          /*
           * Connection retry time-out
           */
          ui_notification_dialog(
              GTK_MESSAGE_WARNING, FALSE, "Connect",
              "Failed to connect to provided host/ip address");
        }

        /*
         * Re-enable connect button
         */
        ui_enable_connect_button();
        operation_running = FALSE;
      }

      ui_callback_dialogbox_connect_destroy();
    }
  }

  return TRUE;
}

gboolean ui_callback_on_disconnect(GtkWidget *widget, gpointer data) {
  g_message("Disconnect event occurred");
  ui_pipe_write_message(ui_main_data.pipe_fd[0], UI_PIPE_DISCONNECT_EVT, NULL,
                        0);

  if (dialogbox_connect != NULL) {
    gtk_dialog_response(GTK_DIALOG(dialogbox_connect), GTK_RESPONSE_CLOSE);
  } else {
    ui_enable_connect_button();
    operation_running = FALSE;
  }

  return TRUE;
}

gboolean ui_callback_signal_go_to_first(GtkWidget *widget, gpointer data) {
  gtk_toggle_tool_button_set_active(
      GTK_TOGGLE_TOOL_BUTTON(ui_main_data.signals_go_to_last_button), FALSE);
  ui_tree_view_select_row(0);
  return TRUE;
}

gboolean ui_callback_signal_go_to(GtkWidget *widget, gpointer data) {
  gtk_window_set_focus(GTK_WINDOW(ui_main_data.window),
                       ui_main_data.signals_go_to_entry);
  return TRUE;
}

gboolean ui_callback_signal_go_to_entry(GtkWidget *widget, gpointer data) {
  gtk_toggle_tool_button_set_active(
      GTK_TOGGLE_TOOL_BUTTON(ui_main_data.signals_go_to_last_button), FALSE);
  // gtk_entry_buffer_set_text(GTK_ENTRY(ui_main_data.signals_go_to_entry), "");
  gtk_window_set_focus(GTK_WINDOW(ui_main_data.window),
                       ui_main_data.messages_list);
  return TRUE;
}

gboolean ui_callback_signal_go_to_last(GtkWidget *widget, gpointer data) {
  gboolean enable = (data != NULL) ? TRUE : FALSE;
  gboolean enabled;

  if (enable) {
    gtk_toggle_tool_button_set_active(
        GTK_TOGGLE_TOOL_BUTTON(ui_main_data.signals_go_to_last_button), TRUE);
    return TRUE;
  }

  enabled = gtk_toggle_tool_button_get_active(
      GTK_TOGGLE_TOOL_BUTTON(ui_main_data.signals_go_to_last_button));
  g_info("Button signal go to last event occurred %d %d", enable, enabled);

  if (enabled) {
    ui_main_data.follow_last = TRUE;
    ui_tree_view_select_row(ui_tree_view_get_filtered_number() - 1);
  } else {
    ui_main_data.follow_last = FALSE;
  }

  return TRUE;
}

gboolean ui_callback_display_message_header(GtkWidget *widget, gpointer data) {
  ui_main_data.display_message_header = !ui_main_data.display_message_header;
  // TODO refresh textview.
  return TRUE;
}

gboolean ui_callback_display_brace(GtkWidget *widget, gpointer data) {
  ui_main_data.display_brace = !ui_main_data.display_brace;
  // TODO refresh textview.
  return TRUE;
}

gboolean ui_callback_signal_clear_list(GtkWidget *widget, gpointer data) {
  /*
   * Disable buttons to move in the list of signals
   */
  ui_set_sensitive_move_buttons(FALSE);
  ui_set_title("");
  /*
   * Clear list of signals
   */
  ui_tree_view_destroy_list(ui_main_data.messages_list);

  if (ui_main_data.text_view != NULL) {
    ui_signal_dissect_clear_view(ui_main_data.text_view);
  }

  ui_notebook_terminal_clear();
  return TRUE;
}

static void ui_callback_on_menu_items_selected(GtkWidget *widget,
                                               gpointer data) {
  gboolean active = data != NULL;

  if (GTK_IS_CHECK_MENU_ITEM(widget)) {
    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), active);
  }
}

gboolean ui_callback_on_menu_none(GtkWidget *widget, gpointer data) {
  GtkWidget *menu = (GtkWidget *)data;

  g_info("ui_callback_on_menu_none occurred %lx %lx)", (long)widget,
         (long)data);
  refresh_message_list = FALSE;
  gtk_container_foreach(GTK_CONTAINER(menu), ui_callback_on_menu_items_selected,
                        (gpointer)FALSE);
  refresh_message_list = TRUE;

  if (filters_changed) {
    ui_tree_view_refilter();
    filters_changed = FALSE;
  }

  return TRUE;
}

gboolean ui_callback_on_menu_all(GtkWidget *widget, gpointer data) {
  GtkWidget *menu = (GtkWidget *)data;

  g_info("ui_callback_on_menu_all occurred %lx %lx)", (long)widget, (long)data);
  refresh_message_list = FALSE;
  gtk_container_foreach(GTK_CONTAINER(menu), ui_callback_on_menu_items_selected,
                        (gpointer)TRUE);
  refresh_message_list = TRUE;

  if (filters_changed) {
    ui_tree_view_refilter();
    filters_changed = FALSE;
  }

  return TRUE;
}

gboolean ui_callback_on_menu_item_selected(GtkWidget *widget, gpointer data) {
  ui_filter_item_t *filter_entry = data;
  gboolean enabled;

  enabled = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));

  if (filter_entry->enabled != enabled) {
    filter_entry->enabled = enabled;

    if (refresh_message_list) {
      ui_tree_view_refilter();
    } else {
      filters_changed = TRUE;
    }
  }

  g_info(
      "ui_callback_on_menu_item_selected occurred %p %p %s %d (%d messages to "
      "display)",
      widget, data, filter_entry->name, enabled,
      ui_tree_view_get_filtered_number());
  return TRUE;
}

gboolean ui_callback_on_tree_column_header_click(GtkWidget *widget,
                                                 gpointer data) {
  col_type_t col = (col_type_t)data;

  g_info("ui_callback_on_tree_column_header_click %d", col);

  switch (col) {
    case COL_MESSAGE:
      ui_show_filter_menu(&ui_main_data.menu_filter_messages,
                          &ui_filters.messages);
      break;

    case COL_FROM_TASK:
      ui_show_filter_menu(&ui_main_data.menu_filter_origin_tasks,
                          &ui_filters.origin_tasks);
      break;

    case COL_TO_TASK:
      ui_show_filter_menu(&ui_main_data.menu_filter_destination_tasks,
                          &ui_filters.destination_tasks);
      break;

    case COL_INSTANCE:
      ui_show_filter_menu(&ui_main_data.menu_filter_instances,
                          &ui_filters.instances);
      break;

    default:
      g_warning(
          "Unknown column filter %d in call to "
          "ui_callback_on_tree_column_header_click",
          col);
      return FALSE;
  }

  return TRUE;
}
